package main

import (
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"text/template"
)

const packageTemplate = `package {{ .PkgName }}`

const importsTemplate = `{{if .Imports }}

import (
{{- range .Imports }}
	{{ . | quote }}
{{- end }}
)
{{- end }}`

const defaultModelTemplate = `{{ template "package" . }}

{{- template "imports" . }}

{{ with .Table -}}
// {{ .Name }} {{ .Comment }}
type {{ .Name }} struct {
{{- if .BaseType }}
	{{ .BaseType.Name }}
{{ end }}
{{- range .Fields }}
	{{ .Name }} {{ .GoType.Name }} {{ backquote }}
	{{- /*gorm*/}}{{ if .PrimaryKey }}gorm:"primaryKey" {{ end }}
	{{- /*json*/}}json:"{{ lcamel .ColumnName }}"
	{{- backquote}}
	{{- if .Comment}} // {{ .Comment }}{{ end }}
{{- end }}
}

func (*{{ .Name }}) TableName() string {
	return {{ quote .TableName }}
}
{{- end }}
`

const defaultGormDaoTemplate = `{{ template "package" . }}

{{- template "imports" . }}

type {{ .DaoName }} struct {
{{- range .Vars.daoFields }}
	{{ . }}
{{- end }}
}

// Create 插入一条 {{ .Table.TableName }} 记录，如果主键是自增，会将主键赋值给实体
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Create(value *{{ .ModelName }}) error {
	return {{ .Vars.getDB }}.Create(value).Error
}

{{- $primaryField := table_get "pk" .Table }}
{{- if $primaryField }}

// Save 插入或更新一条 {{ .Table.TableName }} 记录，如果主键是自增，会将主键赋值给实体，所有字段（包括空值）都会被更新
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Save(value *{{ .ModelName }}) error {
	return {{ .Vars.getDB }}.Save(value).Error
}

// UpdateBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 更新 {{ .Table.TableName }} 记录，updates 可以是 map[string]any 或者结构体，结构体字段中的零值（例如 0, false, "", nil）不会被更新
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) UpdateBy{{ $primaryField.Name }}(updates any, {{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) error {
	return {{ .Vars.getDB }}.Model(&{{ .ModelName }}{}).Where("{{ $primaryField.ColumnName }} = ?", {{ lcamel $primaryField.ColumnName }}).Updates(updates).Error
}

// DeleteBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 删除一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) DeleteBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) error {
	return {{ .Vars.getDB }}.Delete(&{{ .ModelName }}{}, {{ lcamel $primaryField.ColumnName }}).Error
}

// GetBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 获取一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) GetBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) (*{{ .ModelName }}, error) {
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Take(&value, {{ lcamel $primaryField.ColumnName }}).Error
	if err != nil {
		return nil, err
	}
	return &value, err
}

// ListBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 列表获取 {{ .Table.TableName }} 记录列表
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) ListBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }}List []{{ $primaryField.GoType.Name }}) ([]*{{ .ModelName }}, error) {
	if len({{ lcamel $primaryField.ColumnName }}List) == 0 {
		return nil, nil
	}
	var entities []*{{ .ModelName }}
	err := {{ .Vars.getDB }}.Find(&entities, {{ lcamel $primaryField.ColumnName }}List).Error
	if err != nil {
		return nil, err
	}
	return entities, err
}
{{- end }}

// Get 通过 query，conds 构造 where 查询一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Get(query any, conds ...any) (*{{ .ModelName }}, error) {
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Where(query, conds...).Take(&value).Error
	if err != nil {
		return nil, err
	}
	return &value, nil
}

// TryGet 通过 query，conds 构造 where 查询一条 {{ .Table.TableName }} 记录，如果没有找到记录，不会返回错误
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) TryGet(query any, conds ...any) (*{{ .ModelName }}, error) {
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Where(query, conds...).Take(&value).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return &value, nil
}

// List 通过 query，conds 构造 where 获取 {{ .Table.TableName }} 记录列表
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) List(query any, conds ...any) ([]*{{ .ModelName }}, error) {
	var entities []*{{ .ModelName }}
	err := {{ .Vars.getDB }}.Where(query, conds...).Find(&entities).Error
	if err != nil {
		return nil, err
	}
	return entities, nil
}

// Update 通过 query，conds 构造 where 更新 {{ .Table.TableName }} 记录，updates 可以是 map[string]any 或者结构体，结构体字段中的零值（例如 0, false, "", nil）不会被更新
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Update(updates any, query any, conds ...any) error {
	return {{ .Vars.getDB }}.Model(&{{ .ModelName }}{}).Where(query, conds...).Updates(updates).Error
}

// GetBy 通过回调函数 fn 构造查询条件获取一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) GetBy(fn func(db *gorm.DB) *gorm.DB) (*{{ .ModelName }}, error) {
	var value {{ .ModelName }}
	err := fn({{ .Vars.getDB }}).Take(&value).Error
	if err != nil {
		return nil, err
	}
	return &value, nil
}

// ListBy 通过回调函数 fn 构造查询条件获取 {{ .Table.TableName }} 记录列表
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) ListBy(fn func(db *gorm.DB) *gorm.DB) ([]*{{ .ModelName }}, error) {
	var entities []*{{ .ModelName }}
	err := fn({{ .Vars.getDB }}).Find(&entities).Error
	if err != nil {
		return nil, err
	}
	return entities, nil
}
`

const defaultSqlxDaoTemplate = `{{ template "package" . }}

{{- template "imports" . }}

type {{ .DaoName }} struct {
{{- range .Vars.daoFields }}
	{{ . }}
{{- end }}
}

{{ $tableName := sqlreserve .Table.TableName -}}
{{ $softDelete := table_get "del" .Table -}}
{{ $checkDeletedExpr := sqlcheckdel $softDelete -}}
// Create 插入一条 {{ .Table.TableName }} 记录，如果主键是自增，会将主键赋值给实体
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Create(value *{{ .ModelName }}) error {
	const sqlCreate = "insert into {{ $tableName }}({{ table_fields "no_ai" .Table | sqlcolumn | join "," }}) values ({{ smap (fixedf "?") (table_fields "no_ai" .Table | sqlcolumn) | join "," }})"
	res, err := {{ .Vars.getDB }}.Exec(sqlCreate, {{ smap (strfmtf "value.%s") (table_fields "no_ai" .Table | fieldname) | join ", " }})
	if err != nil {
		return err
	}
{{- if table_get "ai" .Table }}
	id, _ := res.LastInsertId()
	value.{{ (table_get "ai" .Table).Name }} = id
{{- else }}
    _ = res
{{- end }}
	return nil
}

{{- if table_fields "def_nopk" .Table | len }}

// CreateDefault 插入一条 {{ .Table.TableName }} 记录，所有默认值的字段将被忽略，如果主键是自增，会将主键赋值给实体
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) CreateDefault(value *{{ .ModelName }}) error {
	const sqlCreateDefault = "insert into {{ $tableName }}({{ table_fields "no_def" .Table | sqlcolumn | join "," }}) values({{ smap (fixedf "?") (table_fields "no_def" .Table | sqlcolumn) | join "," }})"
	res, err := {{ .Vars.getDB }}.Exec(sqlCreateDefault, {{ smap (strfmtf "value.%s") (table_fields "no_def" .Table | fieldname) | join ", " }})
	if err != nil {
		return err
	}
{{- if table_get "ai" .Table }}
	id, _ := res.LastInsertId()
	value.{{ (table_get "ai" .Table).Name }} = id
{{- else }}
    _ = res
{{- end }}
	return nil
}
{{- end }}

{{- $primaryField := table_get "pk" .Table }}
{{- if $primaryField }}
{{- $primaryColumn := sqlreserve $primaryField.ColumnName }}

// Save 插入或更新一条 {{ .Table.TableName }} 记录，如果主键是自增，会将主键赋值给实体，除主键外所有字段（包括空值）都会被更新
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Save(value *{{ .ModelName }}) error {
	if value.{{ $primaryField.Name }} == {{ zeroval $primaryField.GoType }} {
		return dao.Create(value)
	}
	const sqlSave = "update {{ $tableName }} set {{ smap (strfmtf "%s=?") (table_fields "no_pk" .Table | sqlcolumn) | join ", " }} where {{ $primaryColumn }}=?{{ if $checkDeletedExpr }} and {{ $checkDeletedExpr }}{{end}}"
	_, err := {{ .Vars.getDB }}.Exec(sqlSave, {{ smap (strfmtf "value.%s") (table_fields "no_pk" .Table | fieldname) | join ", " }}, value.{{ $primaryField.Name }})
	return err
}

// UpdateBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 更新 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) UpdateBy{{ $primaryField.Name }}(updates map[string]any, {{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) (bool, error) {
	if len(updates) == 0 {
		return false, nil
	}
	ub := sqlbuilder.Update({{ quote .Table.TableName }})
	for k, v := range updates {
		ub.SetMore(ub.Assign(k, v))
	}
	ub.Where(ub.Equal("{{ $primaryField.ColumnName }}", {{ lcamel $primaryField.ColumnName }}){{ if $checkDeletedExpr }}, "{{ $checkDeletedExpr }}"{{end}})
	query, args := ub.Build()
	res, err := {{ .Vars.getDB }}.Exec(query, args...)
	if err != nil {
		return false, err
	}
	affected, _ := res.RowsAffected()
	return affected > 0, nil
}

// DeleteBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 删除一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) DeleteBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) (bool, error) {
{{- if $checkDeletedExpr }}
	const sqlDelete = "update {{ $tableName }} set {{ sqlsetdel $softDelete }} where {{ $primaryColumn }}=? and {{ $checkDeletedExpr }}"
{{- else }}
	const sqlDelete = "delete from {{ $tableName }} where {{ $primaryColumn }}=?"
{{- end }}
	res, err := {{ .Vars.getDB }}.Exec(sqlDelete, {{ lcamel $primaryField.ColumnName }})
	if err != nil {
		return false, err
	}
	affected, _ := res.RowsAffected()
	return affected > 0, nil
}

// GetBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 获取一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) GetBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }} {{ $primaryField.GoType.Name }}) (*{{ .ModelName }}, error) {
	const sqlGet = "select {{ .Table.Fields | sqlcolumn | join "," }} from {{ $tableName }} where {{ $primaryColumn }}=?{{ if $checkDeletedExpr }} and {{ $checkDeletedExpr }}{{end}}"
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Get(&value, sqlGet, {{ lcamel $primaryField.ColumnName }})
	if err != nil {
		return nil, err
	}
	return &value, err
}

// ListBy{{ $primaryField.Name }} 通过 {{ $primaryField.ColumnName }} 列表获取 {{ .Table.TableName }} 记录列表
func ({{ if.Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) ListBy{{ $primaryField.Name }}({{ lcamel $primaryField.ColumnName }}List []{{ $primaryField.GoType.Name }}) ([]*{{ .ModelName }}, error) {
	if len({{ lcamel $primaryField.ColumnName }}List) == 0 {
		return nil, nil
	}
	const sqlList = "select {{ .Table.Fields | sqlcolumn | join "," }} from {{ $tableName }} where {{ $primaryColumn }} in (?){{ if $checkDeletedExpr }} and {{ $checkDeletedExpr }}{{end}}"
	query, args, err := sqlx.In(sqlList, {{ lcamel $primaryField.ColumnName }}List)
	if err != nil {
		return nil, err
	}
	var entities []*{{ .ModelName }}
	err = {{ .Vars.getDB }}.Select(&entities, query, args...)
	if err != nil {
		return nil, err
	}
	return entities, err
}

{{- end }}

// Get 通过 cond, vars 构造 where 查询一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Get(cond string, vars ...any) (*{{ .ModelName }}, error) {
	sb := sqlbuilder.Select({{ smap (strfmtf "%q") (.Table.Fields | columnname) | join ", " }}).From({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}.Limit(1)
	sb.Where(sb.Var(sqlbuilder.Build(cond, vars...)))
	query, args := sb.Build()
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Get(&value, query, args...)
	if err != nil {
		return nil, err
	}
	return &value, err
}

// TryGet 通过 cond, vars 构造 where 查询一条 {{ .Table.TableName }} 记录，如果没有找到记录，不会返回错误
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) TryGet(cond string, vars ...any) (*{{ .ModelName }}, error) {
	sb := sqlbuilder.Select({{ smap (strfmtf "%q") (.Table.Fields | columnname) | join ", " }}).From({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}.Limit(1)
	sb.Where(sb.Var(sqlbuilder.Build(cond, vars...)))
	query, args := sb.Build()
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Get(&value, query, args...)
	if err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, err
	}
	return &value, err
}

// List 通过 cond, vars 构造 where 获取 {{ .Table.TableName }} 记录列表
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) List(cond string, vars ...any) ([]*{{ .ModelName }}, error) {
	sb := sqlbuilder.Select({{ smap (strfmtf "%q") (.Table.Fields | columnname) | join ", " }}).From({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}
	sb.Where(sb.Var(sqlbuilder.Build(cond, vars...)))
	query, args := sb.Build()
	var entities []*{{ .ModelName }}
	err := {{ .Vars.getDB }}.Select(&entities, query, args...)
	if err != nil {
		return nil, err
	}
	return entities, nil
}

// Update 通过 cond, vars 构造 where 更新 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) Update(updates map[string]any, cond string, vars ...any) (int64, error) {
	ub := sqlbuilder.Update({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}
	for k, v := range updates {
		ub.SetMore(ub.Assign(k, v))
	}
	ub.Where(ub.Var(sqlbuilder.Build(cond, vars...)))
	query, args := ub.Build()
	res, err := {{ .Vars.getDB }}.Exec(query, args...)
	if err != nil {
		return 0, err
	}
	affected, _ := res.RowsAffected()
	return affected, nil
}

// GetBy 通过回调函数 fn 构造查询条件获取一条 {{ .Table.TableName }} 记录
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) GetBy(fn func(sb *sqlbuilder.SelectBuilder) *sqlbuilder.SelectBuilder) (*{{ .ModelName }}, error) {
	sb := sqlbuilder.Select({{ smap (strfmtf "%q") (.Table.Fields | columnname) | join ", " }}).From({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}.Limit(1)
	sb = fn(sb)
	query, args := sb.Build()
	var value {{ .ModelName }}
	err := {{ .Vars.getDB }}.Get(&value, query, args...)
	if err != nil {
		return nil, err
	}
	return &value, err
}

// ListBy 通过回调函数 fn 构造查询条件获取 {{ .Table.TableName }} 记录列表
func ({{ if .Vars.receiver }}{{ .Vars.receiver }} {{end}}*{{ .DaoName }}) ListBy(fn func(sb *sqlbuilder.SelectBuilder) *sqlbuilder.SelectBuilder) ([]*{{ .ModelName }}, error) {
	sb := sqlbuilder.Select({{ smap (strfmtf "%q") (.Table.Fields | columnname) | join ", " }}).From({{ quote .Table.TableName }}){{if $checkDeletedExpr }}.Where("{{ $checkDeletedExpr }}"){{end}}
	sb = fn(sb)
	query, args := sb.Build()
	var entities []*{{ .ModelName }}
	err := {{ .Vars.getDB }}.Select(&entities, query, args...)
	if err != nil {
		return nil, err
	}
	return entities, nil
}
`

var rootTemplate = func() *template.Template {
	t := template.New("").Funcs(template.FuncMap{
		"quote": strconv.Quote,
		"lcamel": func(s string) string {
			return camelCase(s, true)
		},
		"ucamel": func(s string) string {
			return camelCase(s, false)
		},
		"backquote": func() string { return "`" },
		"trim":      strings.Trim,
		"repleace":  strings.Replace,
		"tolower":   strings.ToLower,
		"toupper":   strings.ToUpper,
		"regex_replace": func(s string, pattern string, replace string) string {
			re := regexp.MustCompile(pattern)
			return re.ReplaceAllString(s, replace)
		},
		"join":  func(sep string, s []string) string { return strings.Join(s, sep) },
		"split": func(sep string, s string) []string { return strings.Split(s, sep) },
		"table_fields": func(op string, t *TableDesc) []*TableField {
			var r []*TableField
			switch op {
			case "no_ai":
				for _, f := range t.Fields {
					if !f.AutoInc {
						r = append(r, f)
					}
				}
			case "no_pk":
				for _, f := range t.Fields {
					if !f.PrimaryKey {
						r = append(r, f)
					}
				}
			case "def_nopk":
				for _, f := range t.Fields {
					if f.DefaultValue && !f.PrimaryKey {
						r = append(r, f)
					}
				}
			case "no_def":
				for _, f := range t.Fields {
					if !f.DefaultValue {
						r = append(r, f)
					}
				}
			default:
			}
			return r
		},
		"table_get": func(op string, t *TableDesc) *TableField {
			for _, f := range t.Fields {
				switch op {
				case "ai":
					if f.AutoInc {
						return f
					}
				case "pk":
					if f.PrimaryKey {
						return f
					}
				case "del":
					if f.SoftDelete {
						return f
					}
				}
			}
			return nil
		},
		"fieldname": func(f any) any {
			switch f := f.(type) {
			case *TableField:
				if f == nil {
					return ""
				}
				return f.Name
			case []*TableField:
				r := make([]string, len(f))
				for i, v := range f {
					r[i] = v.Name
				}
				return r
			case nil:
				return ""
			default:
				panic("unsupported type")
			}
		},
		"columnname": func(f any) any {
			switch f := f.(type) {
			case *TableField:
				if f == nil {
					return ""
				}
				return f.ColumnName
			case []*TableField:
				r := make([]string, len(f))
				for i, v := range f {
					r[i] = v.ColumnName
				}
				return r
			case nil:
				return ""
			default:
				panic("unsupported type")
			}
		},
		"sqlcheckdel": func(f *TableField) string {
			if f == nil {
				return ""
			}
			if f.Nullable {
				return fmt.Sprintf("%s IS NULL", reserveSqlName(f.ColumnName))
			}
			switch f.DbType {
			case "tinyint", "int", "bigint":
				return fmt.Sprintf("%s=0", reserveSqlName(f.ColumnName))
			case "varchar":
				return fmt.Sprintf("%s=''", reserveSqlName(f.ColumnName))
			default:
				panic("unsupported type")
			}
		},
		"sqlsetdel": func(f *TableField) string {
			if f == nil {
				return ""
			}
			switch f.DbType {
			case "tinyint":
				return fmt.Sprintf("%s=1", reserveSqlName(f.ColumnName))
			case "int", "bigint":
				if strings.HasSuffix(f.ColumnName, "_time") || strings.HasSuffix(f.ColumnName, "_at") {
					return fmt.Sprintf("%s=UNIX_TIMESTAMP(CURRENT_TIMESTAMP)", reserveSqlName(f.ColumnName))
				} else {
					return fmt.Sprintf("%s=1", reserveSqlName(f.ColumnName))
				}
			case "varchar":
				return fmt.Sprintf("%s=''", reserveSqlName(f.ColumnName))
			case "datetime", "timestamp":
				return fmt.Sprintf("%s=CURRENT_TIMESTAMP", reserveSqlName(f.ColumnName))
			default:
				panic("unsupported type")
			}
		},
		"sqlreserve": reserveSqlName,
		"sqlcolumn": func(f any) any {
			switch f := f.(type) {
			case *TableField:
				if f == nil {
					return ""
				}
				return reserveSqlName(f.ColumnName)
			case []*TableField:
				r := make([]string, len(f))
				for i, v := range f {
					r[i] = reserveSqlName(v.ColumnName)
				}
				return r
			case nil:
				return ""
			default:
				panic("unsupported type")
			}
		},
		"fixedf": func(s string) func(string) string {
			return func(_ string) string {
				return s
			}
		},
		"strfmtf": func(format string) func(string) string {
			return func(s string) string {
				return fmt.Sprintf(format, s)
			}
		},
		"smap": func(f func(string) string, s []string) []string {
			r := make([]string, len(s))
			for i, v := range s {
				r[i] = f(v)
			}
			return r
		},
		"zeroval": func(ty *GoType) string {
			switch ty.Name {
			case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
				return "0"
			case "float32", "float64":
				return "0.0"
			case "string":
				return "\"\""
			case "bool":
				return "false"
			case "time.Time":
				return "time.Time{}"
			default:
				if strings.HasPrefix(ty.Name, "*") || strings.HasPrefix(ty.Name, "[]") || strings.HasPrefix(ty.Name, "map[") {
					return "nil"
				}
				panic("unsupported type")
			}
		},
	})
	template.Must(t.New("package").Parse(packageTemplate))
	template.Must(t.New("imports").Parse(importsTemplate))
	template.Must(t.New("default_model").Parse(defaultModelTemplate))
	template.Must(t.New("gorm_dao").Parse(defaultGormDaoTemplate))
	template.Must(t.New("sqlx_dao").Parse(defaultSqlxDaoTemplate))
	return t
}()

func tryLoadTemplateFile(fname string) error {
	data, err := os.ReadFile(fname)
	if err != nil {
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}
	_, err = rootTemplate.New(filepath.ToSlash(fname)).Parse(string(data))
	return err
}
